/*
 * Copyright (c) 2009 Haefelinger IT 
 *
 * Licensed  under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required  by  applicable  law  or  agreed  to in writing, 
 * software distributed under the License is distributed on an "AS 
 * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
 * express or implied.
 
 * See the License for the specific language governing permissions
 * and limitations under the License.
 */

//
// (c) 2000 Sun Microsystems, Inc.
// ALL RIGHTS RESERVED
// 
// License Grant-
// 
// 
// Permission to use, copy, modify, and distribute this Software and its
// documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee is
// hereby granted.
// 
// This Software is provided "AS IS". All express warranties, including any
// implied warranty of merchantability, satisfactory quality, fitness for a
// particular purpose, or non-infringement, are disclaimed, except to the extent
// that such disclaimers are held to be legally invalid.
// 
// You acknowledge that Software is not designed, licensed or intended for use
// in
// the design, construction, operation or maintenance of any nuclear facility
// ("High Risk Activities"). Sun disclaims any express or implied warranty of
// fitness for such uses.
//
// Please refer to the loc http://www.sun.com/policies/trademarks/ for further
// important trademark information and to
// http://java.sun.com/nav/business/index.html for further important licensing
// information for the Java Technology.
//
package it.haefelinger.flaka.util;

import java.text.DecimalFormatSymbols;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Vector;

/**
 * PrintfFormat allows the formatting of an array of objects embedded within a
 * string. Primitive types must be passed using wrapper types. The formatting is
 * controlled by a control string.
 * <p>
 * A control string is a Java string that contains a control specification. The
 * control specification starts at the first percent sign (%) in the string,
 * provided that this percent sign
 * <ol>
 * <li>is not escaped protected by a matching % or is not an escape %
 * character,
 * <li>is not at the end of the format string, and
 * <li>precedes a sequence of characters that parses as a valid control
 * specification.
 * </ol>
 * </p>
 * <p>
 * A control specification usually takes the form:
 * 
 * <pre>
 *  % ['-+ #0]* [0..9]* { . [0..9]* }+
 *                { [hlL] }+ [idfgGoxXeEcs]
 * </pre>
 * 
 * There are variants of this basic form that are discussed below.
 * </p>
 * <p>
 * The format is composed of zero or more directives defined as follows:
 * <ul>
 * <li>ordinary characters, which are simply copied to the output stream;
 * <li>escape sequences, which represent non-graphic characters; and
 * <li>conversion specifications, each of which results in the fetching of zero
 * or more arguments.
 * </ul>
 * </p>
 * <p>
 * The results are undefined if there are insufficient arguments for the format.
 * Usually an unchecked exception will be thrown. If the format is exhausted
 * while arguments remain, the excess arguments are evaluated but are otherwise
 * ignored. In format strings containing the % form of conversion
 * specifications, each argument in the argument list is used exactly once.
 * </p>
 * <p>
 * Conversions can be applied to the <code>n</code>th argument after the
 * format in the argument list, rather than to the next unused argument. In this
 * case, the conversion characer % is replaced by the sequence %<code>n</code>$,
 * where <code>n</code> is a decimal integer giving the position of the
 * argument in the argument list.
 * </p>
 * <p>
 * In format strings containing the %<code>n</code>$ form of conversion
 * specifications, each argument in the argument list is used exactly once.
 * </p>
 * 
 * <h4>Escape Sequences</h4>
 * <p>
 * The following table lists escape sequences and associated actions on display
 * devices capable of the action. <table>
 * <tr>
 * <th align=left>Sequence</th>
 * <th align=left>Name</th>
 * <th align=left>Description</th>
 * </tr>
 * <tr>
 * <td>\\</td>
 * <td>backlash</td>
 * <td>None. </td>
 * </tr>
 * <tr>
 * <td>\a</td>
 * <td>alert</td>
 * <td>Attempts to alert the user through audible or visible notification.
 * </td>
 * </tr>
 * <tr>
 * <td>\b</td>
 * <td>backspace</td>
 * <td>Moves the printing position to one column before the current position,
 * unless the current position is the start of a line. </td>
 * </tr>
 * <tr>
 * <td>\f</td>
 * <td>form-feed</td>
 * <td>Moves the printing position to the initial printing position of the next
 * logical page. </td>
 * </tr>
 * <tr>
 * <td>\n</td>
 * <td>newline</td>
 * <td>Moves the printing position to the start of the next line. </td>
 * </tr>
 * <tr>
 * <td>\r</td>
 * <td>carriage-return</td>
 * <td>Moves the printing position to the start of the current line. </td>
 * </tr>
 * <tr>
 * <td>\t</td>
 * <td>tab</td>
 * <td>Moves the printing position to the next implementation- defined
 * horizontal tab position. </td>
 * </tr>
 * <tr>
 * <td>\v</td>
 * <td>vertical-tab</td>
 * <td>Moves the printing position to the start of the next
 * implementation-defined vertical tab position. </td>
 * </tr>
 * </table>
 * </p>
 * <h4>Conversion Specifications</h4>
 * <p>
 * Each conversion specification is introduced by the percent sign character
 * (%). After the character %, the following appear in sequence:
 * </p>
 * <p>
 * Zero or more flags (in any order), which modify the meaning of the conversion
 * specification.
 * </p>
 * <p>
 * An optional minimum field width. If the converted value has fewer characters
 * than the field width, it will be padded with spaces by default on the left; t
 * will be padded on the right, if the left- adjustment flag (-), described
 * below, is given to the field width. The field width takes the form of a
 * decimal integer. If the conversion character is s, the field width is the the
 * minimum number of characters to be printed.
 * </p>
 * <p>
 * An optional precision that gives the minumum number of digits to appear for
 * the d, i, o, x or X conversions (the field is padded with leading zeros); the
 * number of digits to appear after the radix character for the e, E, and f
 * conversions, the maximum number of significant digits for the g and G
 * conversions; or the maximum number of characters to be written from a string
 * is s and S conversions. The precision takes the form of an optional decimal
 * digit string, where a null digit string is treated as 0. If a precision
 * appears with a c conversion character the precision is ignored.
 * </p>
 * <p>
 * An optional h specifies that a following d, i, o, x, or X conversion
 * character applies to a type short argument (the argument will be promoted
 * according to the integral promotions and its value converted to type short
 * before printing).
 * </p>
 * <p>
 * An optional l (ell) specifies that a following d, i, o, x, or X conversion
 * character applies to a type long argument.
 * </p>
 * <p>
 * A field width or precision may be indicated by an asterisk (*) instead of a
 * digit string. In this case, an integer argument supplised the field width
 * precision. The argument that is actually converted is not fetched until the
 * conversion letter is seen, so the the arguments specifying field width or
 * precision must appear before the argument (if any) to be converted. If the
 * precision argument is negative, it will be changed to zero. A negative field
 * width argument is taken as a - flag, followed by a positive field width.
 * </p>
 * <p>
 * In format strings containing the %<code>n</code>$ form of a conversion
 * specification, a field width or precision may be indicated by the sequence *<code>m</code>$,
 * where m is a decimal integer giving the position in the argument list (after
 * the format argument) of an integer argument containing the field width or
 * precision.
 * </p>
 * <p>
 * The format can contain either numbered argument specifications (that is, %<code>n</code>$
 * and *<code>m</code>$), or unnumbered argument specifications (that is %
 * and *), but normally not both. The only exception to this is that %% can be
 * mixed with the %<code>n</code>$ form. The results of mixing numbered and
 * unnumbered argument specifications in a format string are undefined.
 * </p>
 * 
 * <h4>Flag Characters</h4>
 * <p>
 * The flags and their meanings are:
 * </p>
 * <dl>
 * <dt>'
 * <dd> integer portion of the result of a decimal conversion (%i, %d, %f, %g,
 * or %G) will be formatted with thousands' grouping characters. For other
 * conversions the flag is ignored. The non-monetary grouping character is used.
 * <dt>-
 * <dd> result of the conversion is left-justified within the field. (It will be
 * right-justified if this flag is not specified).</td>
 * </tr>
 * <dt>+
 * <dd> result of a signed conversion always begins with a sign (+ or -). (It
 * will begin with a sign only when a negative value is converted if this flag
 * is not specified.)
 * <dt>&lt;space&gt;
 * <dd> If the first character of a signed conversion is not a sign, a space
 * character will be placed before the result. This means that if the space
 * character and + flags both appear, the space flag will be ignored.
 * <dt>#
 * <dd> value is to be converted to an alternative form. For c, d, i, and s
 * conversions, the flag has no effect. For o conversion, it increases the
 * precision to force the first digit of the result to be a zero. For x or X
 * conversion, a non-zero result has 0x or 0X prefixed to it, respectively. For
 * e, E, f, g, and G conversions, the result always contains a radix character,
 * even if no digits follow the radix character (normally, a decimal point
 * appears in the result of these conversions only if a digit follows it). For g
 * and G conversions, trailing zeros will not be removed from the result as they
 * normally are.
 * <dt>0
 * <dd> d, i, o, x, X, e, E, f, g, and G conversions, leading zeros (following
 * any indication of sign or base) are used to pad to the field width; no space
 * padding is performed. If the 0 and - flags both appear, the 0 flag is
 * ignored. For d, i, o, x, and X conversions, if a precision is specified, the
 * 0 flag will be ignored. For c conversions, the flag is ignored.
 * </dl>
 * 
 * <h4>Conversion Characters</h4>
 * <p>
 * Each conversion character results in fetching zero or more arguments. The
 * results are undefined if there are insufficient arguments for the format.
 * Usually, an unchecked exception will be thrown. If the format is exhausted
 * while arguments remain, the excess arguments are ignored.
 * </p>
 * 
 * <p>
 * The conversion characters and their meanings are:
 * </p>
 * <dl>
 * <dt>d,i
 * <dd>The int argument is converted to a signed decimal in the style [-]dddd.
 * The precision specifies the minimum number of digits to appear; if the value
 * being converted can be represented in fewer digits, it will be expanded with
 * leading zeros. The default precision is 1. The result of converting 0 with an
 * explicit precision of 0 is no characters.
 * <dt>o
 * <dd> The int argument is converted to unsigned octal format in the style
 * ddddd. The precision specifies the minimum number of digits to appear; if the
 * value being converted can be represented in fewer digits, it will be expanded
 * with leading zeros. The default precision is 1. The result of converting 0
 * with an explicit precision of 0 is no characters.
 * <dt>x
 * <dd> The int argument is converted to unsigned hexadecimal format in the
 * style dddd; the letters abcdef are used. The precision specifies the minimum
 * numberof digits to appear; if the value being converted can be represented in
 * fewer digits, it will be expanded with leading zeros. The default precision
 * is 1. The result of converting 0 with an explicit precision of 0 is no
 * characters.
 * <dt>X
 * <dd> Behaves the same as the x conversion character except that letters
 * ABCDEF are used instead of abcdef.
 * <dt>f
 * <dd> The floating point number argument is written in decimal notation in the
 * style [-]ddd.ddd, where the number of digits after the radix character (shown
 * here as a decimal point) is equal to the precision specification. A Locale is
 * used to determine the radix character to use in this format. If the precision
 * is omitted from the argument, six digits are written after the radix
 * character; if the precision is explicitly 0 and the # flag is not specified,
 * no radix character appears. If a radix character appears, at least 1 digit
 * appears before it. The value is rounded to the appropriate number of digits.
 * <dt>e,E
 * <dd>The floating point number argument is written in the style
 * [-]d.ddde{+-}dd (the symbols {+-} indicate either a plus or minus sign),
 * where there is one digit before the radix character (shown here as a decimal
 * point) and the number of digits after it is equal to the precision. A Locale
 * is used to determine the radix character to use in this format. When the
 * precision is missing, six digits are written after the radix character; if
 * the precision is 0 and the # flag is not specified, no radix character
 * appears. The E conversion will produce a number with E instead of e
 * introducing the exponent. The exponent always contains at least two digits.
 * However, if the value to be written requires an exponent greater than two
 * digits, additional exponent digits are written as necessary. The value is
 * rounded to the appropriate number of digits.
 * <dt>g,G
 * <dd>The floating point number argument is written in style f or e (or in
 * sytle E in the case of a G conversion character), with the precision
 * specifying the number of significant digits. If the precision is zero, it is
 * taken as one. The style used depends on the value converted: style e (or E)
 * will be used only if the exponent resulting from the conversion is less than
 * -4 or greater than or equal to the precision. Trailing zeros are removed from
 * the result. A radix character appears only if it is followed by a digit.
 * <dt>c,C
 * <dd>The integer argument is converted to a char and the result is written.
 * 
 * <dt>s,S
 * <dd>The argument is taken to be a string and bytes from the string are
 * written until the end of the string or the number of bytes indicated by the
 * precision specification of the argument is reached. If the precision is
 * omitted from the argument, it is taken to be infinite, so all characters up
 * to the end of the string are written.
 * <dt>%
 * <dd>Write a % character; no argument is converted.
 * </dl>
 * <p>
 * If a conversion specification does not match one of the above forms, an
 * IllegalArgumentException is thrown and the instance of PrintfFormat is not
 * created.
 * </p>
 * <p>
 * If a floating point value is the internal representation for infinity, the
 * output is [+]Infinity, where Infinity is either Infinity or Inf, depending on
 * the desired output string length. Printing of the sign follows the rules
 * described above.
 * </p>
 * <p>
 * If a floating point value is the internal representation for "not-a-number,"
 * the output is [+]NaN. Printing of the sign follows the rules described above.
 * </p>
 * <p>
 * In no case does a non-existent or small field width cause truncation of a
 * field; if the result of a conversion is wider than the field width, the field
 * is simply expanded to contain the conversion result.
 * </p>
 * <p>
 * The behavior is like printf. One exception is that the minimum number of
 * exponent digits is 3 instead of 2 for e and E formats when the optional L is
 * used before the e, E, g, or G conversion character. The optional L does not
 * imply conversion to a long long double.
 * </p>
 * <p>
 * The biggest divergence from the C printf specification is in the use of 16
 * bit characters. This allows the handling of characters beyond the small ASCII
 * character set and allows the utility to interoperate correctly with the rest
 * of the Java runtime environment.
 * </p>
 * <p>
 * Omissions from the C printf specification are numerous. All the known
 * omissions are present because Java never uses bytes to represent characters
 * and does not have pointers:
 * </p>
 * <ul>
 * <li>%c is the same as %C.
 * <li>%s is the same as %S.
 * <li>u, p, and n conversion characters.
 * <li>%ws format.
 * <li>h modifier applied to an n conversion character.
 * <li>l (ell) modifier applied to the c, n, or s conversion characters.
 * <li>ll (ell ell) modifier to d, i, o, u, x, or X conversion characters.
 * <li>ll (ell ell) modifier to an n conversion character.
 * <li>c, C, d,i,o,u,x, and X conversion characters apply to Byte, Character,
 * Short, Integer, Long types.
 * <li>f, e, E, g, and G conversion characters apply to Float and Double types.
 * <li>s and S conversion characters apply to String types.
 * <li>All other reference types can be formatted using the s or S conversion
 * characters only.
 * </ul>
 * <p>
 * Most of this specification is quoted from the Unix man page for the sprintf
 * utility.
 * </p>
 * 
 * @author Allan Jacobs
 * @version 1 Release 1: Initial release. Release 2: Asterisk field widths and
 *          precisions %n$ and *m$ Bug fixes g format fix (2 digits in e form
 *          corrupt) rounding in f format implemented round up when digit not
 *          printed is 5 formatting of -0.0f round up/down when last digits are
 *          50000...
 */
public class Printf
{
  /**
   * Constructs an array of control specifications possibly preceded, separated,
   * or followed by ordinary strings. Control strings begin with unpaired
   * percent signs. A pair of successive percent signs designates a single
   * percent sign in the format.
   * 
   * @param fmtArg
   *          Control string.
   * @exception IllegalArgumentException
   *              if the control string is null, zero length, or otherwise
   *              malformed.
   */
  public Printf(String fmtArg) throws IllegalArgumentException
  {
    this(Locale.getDefault(), fmtArg);
  }

  /**
   * Constructs an array of control specifications possibly preceded, separated,
   * or followed by ordinary strings. Control strings begin with unpaired
   * percent signs. A pair of successive percent signs designates a single
   * percent sign in the format.
   * 
   * @param fmtArg
   *          Control string.
   * @exception IllegalArgumentException
   *              if the control string is null, zero length, or otherwise
   *              malformed.
   */
  public Printf(Locale locale, String fmtArg) throws IllegalArgumentException
  {
    this.dfs = new DecimalFormatSymbols(locale);
    int ePos = 0;
    ConversionSpecification sFmt = null;
    String unCS = this.nonControl(fmtArg, 0);
    if (unCS != null)
    {
      sFmt = new ConversionSpecification();
      sFmt.setLiteral(unCS);
      this.vFmt.addElement(sFmt);
    }
    while (this.cPos != -1 && this.cPos < fmtArg.length())
    {
      for (ePos = this.cPos + 1; ePos < fmtArg.length(); ePos++)
      {
        char c = 0;
        c = fmtArg.charAt(ePos);
        if (c == 'i')
          break;
        if (c == 'd')
          break;
        if (c == 'f')
          break;
        if (c == 'g')
          break;
        if (c == 'G')
          break;
        if (c == 'o')
          break;
        if (c == 'x')
          break;
        if (c == 'X')
          break;
        if (c == 'e')
          break;
        if (c == 'E')
          break;
        if (c == 'c')
          break;
        if (c == 's')
          break;
        if (c == '%')
          break;
      }
      ePos = Math.min(ePos + 1, fmtArg.length());
      sFmt = new ConversionSpecification(fmtArg.substring(this.cPos, ePos));
      this.vFmt.addElement(sFmt);
      unCS = this.nonControl(fmtArg, ePos);
      if (unCS != null)
      {
        sFmt = new ConversionSpecification();
        sFmt.setLiteral(unCS);
        this.vFmt.addElement(sFmt);
      }
    }
  }

  /**
   * Return a substring starting at <code>start</code> and ending at either the
   * end of the String <code>s</code>, the next unpaired percent sign, or at the
   * end of the String if the last character is a percent sign.
   * 
   * @param s
   *          Control string.
   * @param start
   *          Position in the string <code>s</code> to begin looking for the
   *          start of a control string.
   * @return the substring from the start position to the beginning of the
   *         control string.
   */
  private String nonControl(String s, int start)
  {
    this.cPos = s.indexOf("%", start);
    if (this.cPos == -1)
      this.cPos = s.length();
    return s.substring(start, this.cPos);
  }

  /**
   * Format an array of objects. Byte, Short, Integer, Long, Float, Double, and
   * Character arguments are treated as wrappers for primitive types.
   * 
   * @param o
   *          The array of objects to format.
   * @return The formatted String.
   */
  public String sprintf(Object[] o)
  {
    Enumeration e = this.vFmt.elements();
    ConversionSpecification cs = null;
    char c = 0;
    int i = 0;
    StringBuffer sb = new StringBuffer();
    while (e.hasMoreElements())
    {
      cs = (ConversionSpecification) e.nextElement();
      c = cs.getConversionCharacter();
      if (c == '\0')
        sb.append(cs.getLiteral());
      else if (c == '%')
        sb.append("%");
      else
      {
        if (cs.isPositionalSpecification())
        {
          i = cs.getArgumentPosition() - 1;
          if (cs.isPositionalFieldWidth())
          {
            int ifw = cs.getArgumentPositionForFieldWidth() - 1;
            cs.setFieldWidthWithArg(((Integer) o[ifw]).intValue());
          }
          if (cs.isPositionalPrecision())
          {
            int ipr = cs.getArgumentPositionForPrecision() - 1;
            cs.setPrecisionWithArg(((Integer) o[ipr]).intValue());
          }
        } else
        {
          if (cs.isVariableFieldWidth())
          {
            cs.setFieldWidthWithArg(((Integer) o[i]).intValue());
            i++;
          }
          if (cs.isVariablePrecision())
          {
            cs.setPrecisionWithArg(((Integer) o[i]).intValue());
            i++;
          }
        }
        if (o[i] instanceof Byte)
          sb.append(cs.internalsprintf(((Byte) o[i]).byteValue()));
        else if (o[i] instanceof Short)
          sb.append(cs.internalsprintf(((Short) o[i]).shortValue()));
        else if (o[i] instanceof Integer)
          sb.append(cs.internalsprintf(((Integer) o[i]).intValue()));
        else if (o[i] instanceof Long)
          sb.append(cs.internalsprintf(((Long) o[i]).longValue()));
        else if (o[i] instanceof Float)
          sb.append(cs.internalsprintf(((Float) o[i]).floatValue()));
        else if (o[i] instanceof Double)
          sb.append(cs.internalsprintf(((Double) o[i]).doubleValue()));
        else if (o[i] instanceof Character)
          sb.append(cs.internalsprintf(((Character) o[i]).charValue()));
        else if (o[i] instanceof String)
          sb.append(cs.internalsprintf((String) o[i]));
        else
          sb.append(cs.internalsprintf(o[i]));
        if (!cs.isPositionalSpecification())
          i++;
      }
    }
    return sb.toString();
  }

  /**
   * Format nothing. Just use the control string.
   * 
   * @return the formatted String.
   */
  public String sprintf()
  {
    Enumeration e = this.vFmt.elements();
    ConversionSpecification cs = null;
    char c = 0;
    StringBuffer sb = new StringBuffer();
    while (e.hasMoreElements())
    {
      cs = (ConversionSpecification) e.nextElement();
      c = cs.getConversionCharacter();
      if (c == '\0')
        sb.append(cs.getLiteral());
      else if (c == '%')
        sb.append("%");
    }
    return sb.toString();
  }

  /**
   * Format an int.
   * 
   * @param x
   *          The int to format.
   * @return The formatted String.
   * @exception IllegalArgumentException
   *              if the conversion character is f, e, E, g, G, s, or S.
   */
  public String sprintf(int x) throws IllegalArgumentException
  {
    Enumeration e = this.vFmt.elements();
    ConversionSpecification cs = null;
    char c = 0;
    StringBuffer sb = new StringBuffer();
    while (e.hasMoreElements())
    {
      cs = (ConversionSpecification) e.nextElement();
      c = cs.getConversionCharacter();
      if (c == '\0')
        sb.append(cs.getLiteral());
      else if (c == '%')
        sb.append("%");
      else
        sb.append(cs.internalsprintf(x));
    }
    return sb.toString();
  }

  /**
   * Format an long.
   * 
   * @param x
   *          The long to format.
   * @return The formatted String.
   * @exception IllegalArgumentException
   *              if the conversion character is f, e, E, g, G, s, or S.
   */
  public String sprintf(long x) throws IllegalArgumentException
  {
    Enumeration e = this.vFmt.elements();
    ConversionSpecification cs = null;
    char c = 0;
    StringBuffer sb = new StringBuffer();
    while (e.hasMoreElements())
    {
      cs = (ConversionSpecification) e.nextElement();
      c = cs.getConversionCharacter();
      if (c == '\0')
        sb.append(cs.getLiteral());
      else if (c == '%')
        sb.append("%");
      else
        sb.append(cs.internalsprintf(x));
    }
    return sb.toString();
  }

  /**
   * Format a double.
   * 
   * @param x
   *          The double to format.
   * @return The formatted String.
   * @exception IllegalArgumentException
   *              if the conversion character is c, C, s, S, d, d, x, X, or o.
   */
  public String sprintf(double x) throws IllegalArgumentException
  {
    Enumeration e = this.vFmt.elements();
    ConversionSpecification cs = null;
    char c = 0;
    StringBuffer sb = new StringBuffer();
    while (e.hasMoreElements())
    {
      cs = (ConversionSpecification) e.nextElement();
      c = cs.getConversionCharacter();
      if (c == '\0')
        sb.append(cs.getLiteral());
      else if (c == '%')
        sb.append("%");
      else
        sb.append(cs.internalsprintf(x));
    }
    return sb.toString();
  }

  /**
   * Format a String.
   * 
   * @param x
   *          The String to format.
   * @return The formatted String.
   * @exception IllegalArgumentException
   *              if the conversion character is neither s nor S.
   */
  public String sprintf(String x) throws IllegalArgumentException
  {
    Enumeration e = this.vFmt.elements();
    ConversionSpecification cs = null;
    char c = 0;
    StringBuffer sb = new StringBuffer();
    while (e.hasMoreElements())
    {
      cs = (ConversionSpecification) e.nextElement();
      c = cs.getConversionCharacter();
      if (c == '\0')
        sb.append(cs.getLiteral());
      else if (c == '%')
        sb.append("%");
      else
        sb.append(cs.internalsprintf(x));
    }
    return sb.toString();
  }

  /**
   * Format an Object. Convert wrapper types to their primitive equivalents and
   * call the appropriate internal formatting method. Convert Strings using an
   * internal formatting method for Strings. Otherwise use the default formatter
   * (use toString).
   * 
   * @param x
   *          the Object to format.
   * @return the formatted String.
   * @exception IllegalArgumentException
   *              if the conversion character is inappropriate for formatting an
   *              unwrapped value.
   */
  public String sprintf(Object x) throws IllegalArgumentException
  {
    Enumeration e = this.vFmt.elements();
    ConversionSpecification cs = null;
    char c = 0;
    StringBuffer sb = new StringBuffer();
    while (e.hasMoreElements())
    {
      cs = (ConversionSpecification) e.nextElement();
      c = cs.getConversionCharacter();
      if (c == '\0')
        sb.append(cs.getLiteral());
      else if (c == '%')
        sb.append("%");
      else
      {
        if (x instanceof Byte)
          sb.append(cs.internalsprintf(((Byte) x).byteValue()));
        else if (x instanceof Short)
          sb.append(cs.internalsprintf(((Short) x).shortValue()));
        else if (x instanceof Integer)
          sb.append(cs.internalsprintf(((Integer) x).intValue()));
        else if (x instanceof Long)
          sb.append(cs.internalsprintf(((Long) x).longValue()));
        else if (x instanceof Float)
          sb.append(cs.internalsprintf(((Float) x).floatValue()));
        else if (x instanceof Double)
          sb.append(cs.internalsprintf(((Double) x).doubleValue()));
        else if (x instanceof Character)
          sb.append(cs.internalsprintf(((Character) x).charValue()));
        else if (x instanceof String)
          sb.append(cs.internalsprintf((String) x));
        else
          sb.append(cs.internalsprintf(x));
      }
    }
    return sb.toString();
  }

  /**
   * <p>
   * ConversionSpecification allows the formatting of a single primitive or
   * object embedded within a string. The formatting is controlled by a format
   * string. Only one Java primitive or object can be formatted at a time.
   * <p>
   * A format string is a Java string that contains a control string. The
   * control string starts at the first percent sign (%) in the string, provided
   * that this percent sign
   * <ol>
   * <li>is not escaped protected by a matching % or is not an escape %
   * character,
   * <li>is not at the end of the format string, and
   * <li>precedes a sequence of characters that parses as a valid control
   * string.
   * </ol>
   * <p>
   * A control string takes the form:
   * 
   * <pre>
   *  % ['-+ #0]* [0..9]* { . [0..9]* }+
   *                { [hlL] }+ [idfgGoxXeEcs]
   * </pre>
   * 
   * <p>
   * The behavior is like printf. One (hopefully the only) exception is that the
   * minimum number of exponent digits is 3 instead of 2 for e and E formats
   * when the optional L is used before the e, E, g, or G conversion character.
   * The optional L does not imply conversion to a long long double.
   */
  private class ConversionSpecification
  {
    /**
     * Constructor. Used to prepare an instance to hold a literal, not a control
     * string.
     */
    ConversionSpecification()
    { /* not used */
    }

    /**
     * Constructor for a conversion specification. The argument must begin with
     * a % and end with the conversion character for the conversion
     * specification.
     * 
     * @param fmtArg
     *          String specifying the conversion specification.
     * @exception IllegalArgumentException
     *              if the input string is null, zero length, or otherwise
     *              malformed.
     */
    ConversionSpecification(String fmtArg) throws IllegalArgumentException
    {
      if (fmtArg == null)
        throw new NullPointerException();
      if (fmtArg.length() == 0)
        throw new IllegalArgumentException("Control strings must have positive" + " lengths.");
      if (fmtArg.charAt(0) == '%')
      {
        this.fmt = fmtArg;
        this.pos = 1;
        setArgPosition();
        setFlagCharacters();
        setFieldWidth();
        setPrecision();
        setOptionalHL();
        if (setConversionCharacter())
        {
          if (this.pos == fmtArg.length())
          {
            if (this.leadingZeros && this.leftJustify)
              this.leadingZeros = false;
            if (this.precisionSet && this.leadingZeros)
            {
              if (this.conversionCharacter == 'd' || this.conversionCharacter == 'i'
                  || this.conversionCharacter == 'o' || this.conversionCharacter == 'x')
              {
                this.leadingZeros = false;
              }
            }
          } else
            throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg);
        } else
          throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg);
      } else
        throw new IllegalArgumentException("Control strings must begin with %.");
    }

    /**
     * Set the String for this instance.
     * 
     * @param s
     *          the String to store.
     */
    void setLiteral(String s)
    {
      this.fmt = s;
    }

    /**
     * Get the String for this instance. Translate any escape sequences.
     * 
     * @return s the stored String.
     */
    String getLiteral()
    {
      StringBuffer sb = new StringBuffer();
      int i = 0;
      while (i < this.fmt.length())
      {
        if (this.fmt.charAt(i) == '\\')
        {
          i++;
          if (i < this.fmt.length())
          {
            char c = this.fmt.charAt(i);
            switch (c)
            {
              case 'a':
                sb.append((char) 0x07);
                break;
              case 'b':
                sb.append('\b');
                break;
              case 'f':
                sb.append('\f');
                break;
              case 'n':
                sb.append(System.getProperty("line.separator"));
                break;
              case 'r':
                sb.append('\r');
                break;
              case 't':
                sb.append('\t');
                break;
              case 'v':
                sb.append((char) 0x0b);
                break;
              case '\\':
                sb.append('\\');
                break;
            }
            i++;
          } else
            sb.append('\\');
        } else
          i++;
      }
      return this.fmt;
    }

    /**
     * Get the conversion character that tells what type of control character
     * this instance has.
     * 
     * @return the conversion character.
     */
    char getConversionCharacter()
    {
      return this.conversionCharacter;
    }

    /**
     * Check whether the specifier has a variable field width that is going to
     * be set by an argument.
     * 
     * @return <code>true</code> if the conversion uses an * field width;
     *         otherwise <code>false</code>.
     */
    boolean isVariableFieldWidth()
    {
      return this.variableFieldWidth;
    }

    /**
     * Set the field width with an argument. A negative field width is taken as
     * a - flag followed by a positive field width.
     * 
     * @param fw
     *          the field width.
     */
    void setFieldWidthWithArg(int fw)
    {
      if (fw < 0)
        this.leftJustify = true;
      this.fieldWidthSet = true;
      this.fieldWidth = Math.abs(fw);
    }

    /**
     * Check whether the specifier has a variable precision that is going to be
     * set by an argument.
     * 
     * @return <code>true</code> if the conversion uses an * precision;
     *         otherwise <code>false</code>.
     */
    boolean isVariablePrecision()
    {
      return this.variablePrecision;
    }

    /**
     * Set the precision with an argument. A negative precision will be changed
     * to zero.
     * 
     * @param pr
     *          the precision.
     */
    void setPrecisionWithArg(int pr)
    {
      this.precisionSet = true;
      this.precision = Math.max(pr, 0);
    }

    /**
     * Format an int argument using this conversion specification.
     * 
     * @param s
     *          the int to format.
     * @return the formatted String.
     * @exception IllegalArgumentException
     *              if the conversion character is f, e, E, g, or G.
     */
    String internalsprintf(int s) throws IllegalArgumentException
    {
      String s2 = "";
      switch (this.conversionCharacter)
      {
        case 'd':
        case 'i':
          if (this.optionalh)
            s2 = printDFormat((short) s);
          else if (this.optionall)
            s2 = printDFormat((long) s);
          else
            s2 = printDFormat(s);
          break;
        case 'x':
        case 'X':
          if (this.optionalh)
            s2 = printXFormat((short) s);
          else if (this.optionall)
            s2 = printXFormat((long) s);
          else
            s2 = printXFormat(s);
          break;
        case 'o':
          if (this.optionalh)
            s2 = printOFormat((short) s);
          else if (this.optionall)
            s2 = printOFormat((long) s);
          else
            s2 = printOFormat(s);
          break;
        case 'c':
        case 'C':
          s2 = printCFormat((char) s);
          break;
        default:
          throw new IllegalArgumentException("Cannot format a int with a format using a "
              + this.conversionCharacter + " conversion character.");
      }
      return s2;
    }

    /**
     * Format a long argument using this conversion specification.
     * 
     * @param s
     *          the long to format.
     * @return the formatted String.
     * @exception IllegalArgumentException
     *              if the conversion character is f, e, E, g, or G.
     */
    String internalsprintf(long s) throws IllegalArgumentException
    {
      String s2 = "";
      switch (this.conversionCharacter)
      {
        case 'd':
        case 'i':
          if (this.optionalh)
            s2 = printDFormat((short) s);
          else if (this.optionall)
            s2 = printDFormat(s);
          else
            s2 = printDFormat((int) s);
          break;
        case 'x':
        case 'X':
          if (this.optionalh)
            s2 = printXFormat((short) s);
          else if (this.optionall)
            s2 = printXFormat(s);
          else
            s2 = printXFormat((int) s);
          break;
        case 'o':
          if (this.optionalh)
            s2 = printOFormat((short) s);
          else if (this.optionall)
            s2 = printOFormat(s);
          else
            s2 = printOFormat((int) s);
          break;
        case 'c':
        case 'C':
          s2 = printCFormat((char) s);
          break;
        default:
          throw new IllegalArgumentException("Cannot format a long with a format using a "
              + this.conversionCharacter + " conversion character.");
      }
      return s2;
    }

    /**
     * Format a double argument using this conversion specification.
     * 
     * @param s
     *          the double to format.
     * @return the formatted String.
     * @exception IllegalArgumentException
     *              if the conversion character is c, C, s, S, i, d, x, X, or o.
     */
    String internalsprintf(double s) throws IllegalArgumentException
    {
      String s2 = "";
      switch (this.conversionCharacter)
      {
        case 'f':
          s2 = printFFormat(s);
          break;
        case 'E':
        case 'e':
          s2 = printEFormat(s);
          break;
        case 'G':
        case 'g':
          s2 = printGFormat(s);
          break;
        default:
          throw new IllegalArgumentException("Cannot " + "format a double with a format using a "
              + this.conversionCharacter + " conversion character.");
      }
      return s2;
    }

    /**
     * Format a String argument using this conversion specification.
     * 
     * @param s
     *          the String to format.
     * @return the formatted String.
     * @exception IllegalArgumentException
     *              if the conversion character is neither s nor S.
     */
    String internalsprintf(String s) throws IllegalArgumentException
    {
      String s2 = "";
      if (this.conversionCharacter == 's' || this.conversionCharacter == 'S')
        s2 = printSFormat(s);
      else
        throw new IllegalArgumentException("Cannot " + "format a String with a format using a "
            + this.conversionCharacter + " conversion character.");
      return s2;
    }

    /**
     * Format an Object argument using this conversion specification.
     * 
     * @param s
     *          the Object to format.
     * @return the formatted String.
     * @exception IllegalArgumentException
     *              if the conversion character is neither s nor S.
     */
    String internalsprintf(Object s)
    {
      String s2 = "";
      if (this.conversionCharacter == 's' || this.conversionCharacter == 'S')
        s2 = printSFormat(s.toString());
      else
        throw new IllegalArgumentException("Cannot format a String with a format using" + " a "
            + this.conversionCharacter + " conversion character.");
      return s2;
    }

    /**
     * For f format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. '+' character means that the conversion will always begin with a
     * sign (+ or -). The blank flag character means that a non-negative input
     * will be preceded with a blank. If both a '+' and a ' ' are specified, the
     * blank flag is ignored. The '0' flag character implies that padding to the
     * field width will be done with zeros instead of blanks.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the number of digits to appear after the radix
     * character. Padding is with trailing 0s.
     */
    private char[] fFormatDigits(double x)
    {
      // int defaultDigits=6;
      String sx;
      int i, j, k;
      int n1In, n2In;
      int expon = 0;
      boolean minusSign = false;
      if (x > 0.0)
        sx = Double.toString(x);
      else if (x < 0.0)
      {
        sx = Double.toString(-x);
        minusSign = true;
      } else
      {
        sx = Double.toString(x);
        if (sx.charAt(0) == '-')
        {
          minusSign = true;
          sx = sx.substring(1);
        }
      }
      int ePos = sx.indexOf('E');
      int rPos = sx.indexOf('.');
      if (rPos != -1)
        n1In = rPos;
      else if (ePos != -1)
        n1In = ePos;
      else
        n1In = sx.length();
      if (rPos != -1)
      {
        if (ePos != -1)
          n2In = ePos - rPos - 1;
        else
          n2In = sx.length() - rPos - 1;
      } else
        n2In = 0;
      if (ePos != -1)
      {
        int ie = ePos + 1;
        expon = 0;
        if (sx.charAt(ie) == '-')
        {
          for (++ie; ie < sx.length(); ie++)
            if (sx.charAt(ie) != '0')
              break;
          if (ie < sx.length())
            expon = -Integer.parseInt(sx.substring(ie));
        } else
        {
          if (sx.charAt(ie) == '+')
            ++ie;
          for (; ie < sx.length(); ie++)
            if (sx.charAt(ie) != '0')
              break;
          if (ie < sx.length())
            expon = Integer.parseInt(sx.substring(ie));
        }
      }
      int p;
      if (this.precisionSet)
        p = this.precision;
      else
        p = defaultDigits - 1;
      char[] ca1 = sx.toCharArray();
      char[] ca2 = new char[n1In + n2In];
      char[] ca3, ca4, ca5;
      for (j = 0; j < n1In; j++)
        ca2[j] = ca1[j];
      i = j + 1;
      for (k = 0; k < n2In; j++, i++, k++)
        ca2[j] = ca1[i];
      if (n1In + expon <= 0)
      {
        ca3 = new char[-expon + n2In];
        for (j = 0, k = 0; k < (-n1In - expon); k++, j++)
          ca3[j] = '0';
        for (i = 0; i < (n1In + n2In); i++, j++)
          ca3[j] = ca2[i];
      } else
        ca3 = ca2;
      boolean carry = false;
      if (p < -expon + n2In)
      {
        if (expon < 0)
          i = p;
        else
          i = p + n1In;
        carry = checkForCarry(ca3, i);
        if (carry)
          carry = startSymbolicCarry(ca3, i - 1, 0);
      }
      if (n1In + expon <= 0)
      {
        ca4 = new char[2 + p];
        if (!carry)
          ca4[0] = '0';
        else
          ca4[0] = '1';
        if (this.alternateForm || !this.precisionSet || this.precision != 0)
        {
          ca4[1] = '.';
          for (i = 0, j = 2; i < Math.min(p, ca3.length); i++, j++)
            ca4[j] = ca3[i];
          for (; j < ca4.length; j++)
            ca4[j] = '0';
        }
      } else
      {
        if (!carry)
        {
          if (this.alternateForm || !this.precisionSet || this.precision != 0)
            ca4 = new char[n1In + expon + p + 1];
          else
            ca4 = new char[n1In + expon];
          j = 0;
        } else
        {
          if (this.alternateForm || !this.precisionSet || this.precision != 0)
            ca4 = new char[n1In + expon + p + 2];
          else
            ca4 = new char[n1In + expon + 1];
          ca4[0] = '1';
          j = 1;
        }
        for (i = 0; i < Math.min(n1In + expon, ca3.length); i++, j++)
          ca4[j] = ca3[i];
        for (; i < n1In + expon; i++, j++)
          ca4[j] = '0';
        if (this.alternateForm || !this.precisionSet || this.precision != 0)
        {
          ca4[j] = '.';
          j++;
          for (k = 0; i < ca3.length && k < p; i++, j++, k++)
            ca4[j] = ca3[i];
          for (; j < ca4.length; j++)
            ca4[j] = '0';
        }
      }
      int nZeros = 0;
      if (!this.leftJustify && this.leadingZeros)
      {
        int xThousands = 0;
        if (this.thousands)
        {
          int xlead = 0;
          if (ca4[0] == '+' || ca4[0] == '-' || ca4[0] == ' ')
            xlead = 1;
          int xdp = xlead;
          for (; xdp < ca4.length; xdp++)
            if (ca4[xdp] == '.')
              break;
          xThousands = (xdp - xlead) / 3;
        }
        if (this.fieldWidthSet)
          nZeros = this.fieldWidth - ca4.length;
        if ((!minusSign && (this.leadingSign || this.leadingSpace)) || minusSign)
          nZeros--;
        nZeros -= xThousands;
        if (nZeros < 0)
          nZeros = 0;
      }
      j = 0;
      if ((!minusSign && (this.leadingSign || this.leadingSpace)) || minusSign)
      {
        ca5 = new char[ca4.length + nZeros + 1];
        j++;
      } else
        ca5 = new char[ca4.length + nZeros];
      if (!minusSign)
      {
        if (this.leadingSign)
          ca5[0] = '+';
        if (this.leadingSpace)
          ca5[0] = ' ';
      } else
        ca5[0] = '-';
      for (i = 0; i < nZeros; i++, j++)
        ca5[j] = '0';
      for (i = 0; i < ca4.length; i++, j++)
        ca5[j] = ca4[i];

      int lead = 0;
      if (ca5[0] == '+' || ca5[0] == '-' || ca5[0] == ' ')
        lead = 1;
      int dp = lead;
      for (; dp < ca5.length; dp++)
        if (ca5[dp] == '.')
          break;
      int nThousands = (dp - lead) / 3;
      // Localize the decimal point.
      if (dp < ca5.length)
        ca5[dp] = Printf.this.dfs.getDecimalSeparator();
      char[] ca6 = ca5;
      if (this.thousands && nThousands > 0)
      {
        ca6 = new char[ca5.length + nThousands + lead];
        ca6[0] = ca5[0];
        for (i = lead, k = lead; i < dp; i++)
        {
          if (i > 0 && (dp - i) % 3 == 0)
          {
            // ca6[k]=',';
            ca6[k] = Printf.this.dfs.getGroupingSeparator();
            ca6[k + 1] = ca5[i];
            k += 2;
          } else
          {
            ca6[k] = ca5[i];
            k++;
          }
        }
        for (; i < ca5.length; i++, k++)
        {
          ca6[k] = ca5[i];
        }
      }
      return ca6;
    }

    /**
     * An intermediate routine on the way to creating an f format String. The
     * method decides whether the input double value is an infinity,
     * not-a-number, or a finite double and formats each type of input
     * appropriately.
     * 
     * @param x
     *          the double value to be formatted.
     * @return the converted double value.
     */
    private String fFormatString(double x)
    {
      char[] ca6, ca7;
      if (Double.isInfinite(x))
      {
        if (x == Double.POSITIVE_INFINITY)
        {
          if (this.leadingSign)
            ca6 = "+Inf".toCharArray();
          else if (this.leadingSpace)
            ca6 = " Inf".toCharArray();
          else
            ca6 = "Inf".toCharArray();
        } else
          ca6 = "-Inf".toCharArray();
      } else if (Double.isNaN(x))
      {
        if (this.leadingSign)
          ca6 = "+NaN".toCharArray();
        else if (this.leadingSpace)
          ca6 = " NaN".toCharArray();
        else
          ca6 = "NaN".toCharArray();
      } else
        ca6 = fFormatDigits(x);
      ca7 = applyFloatPadding(ca6, false);
      return new String(ca7);
    }

    /**
     * For e format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. '+' character means that the conversion will always begin with a
     * sign (+ or -). The blank flag character means that a non-negative input
     * will be preceded with a blank. If both a '+' and a ' ' are specified, the
     * blank flag is ignored. The '0' flag character implies that padding to the
     * field width will be done with zeros instead of blanks.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the minimum number of digits to appear after
     * the radix character. Padding is with trailing 0s.
     * 
     * The behavior is like printf. One (hopefully the only) exception is that
     * the minimum number of exponent digits is 3 instead of 2 for e and E
     * formats when the optional L is used before the e, E, g, or G conversion
     * character. The optional L does not imply conversion to a long long
     * double.
     */
    private char[] eFormatDigits(double x, char eChar)
    {
      char[] ca1, ca2, ca3;
      // int defaultDigits=6;
      String sx;
      int i, j, k, p;
      int expon = 0;
      int ePos, rPos, eSize;
      boolean minusSign = false;
      if (x > 0.0)
        sx = Double.toString(x);
      else if (x < 0.0)
      {
        sx = Double.toString(-x);
        minusSign = true;
      } else
      {
        sx = Double.toString(x);
        if (sx.charAt(0) == '-')
        {
          minusSign = true;
          sx = sx.substring(1);
        }
      }
      ePos = sx.indexOf('E');
      if (ePos == -1)
        ePos = sx.indexOf('e');
      rPos = sx.indexOf('.');
      if (rPos != -1)
      {
        /* do nothing */
      } else if (ePos != -1)
      {
        /* do nothing */
      } else
      {
        /* do nothing */
      }
      if (rPos != -1)
      {
        if (ePos != -1)
        {
          /* do nothing */
        } else
        {
          /* do nothing */
        }
      } else
      {
        /* do nothing */
      }
      if (ePos != -1)
      {
        int ie = ePos + 1;
        expon = 0;
        if (sx.charAt(ie) == '-')
        {
          for (++ie; ie < sx.length(); ie++)
            if (sx.charAt(ie) != '0')
              break;
          if (ie < sx.length())
            expon = -Integer.parseInt(sx.substring(ie));
        } else
        {
          if (sx.charAt(ie) == '+')
            ++ie;
          for (; ie < sx.length(); ie++)
            if (sx.charAt(ie) != '0')
              break;
          if (ie < sx.length())
            expon = Integer.parseInt(sx.substring(ie));
        }
      }
      if (rPos != -1)
        expon += rPos - 1;
      if (this.precisionSet)
        p = this.precision;
      else
        p = defaultDigits - 1;
      if (rPos != -1 && ePos != -1)
        ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1, ePos)).toCharArray();
      else if (rPos != -1)
        ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1)).toCharArray();
      else if (ePos != -1)
        ca1 = sx.substring(0, ePos).toCharArray();
      else
        ca1 = sx.toCharArray();
      boolean carry = false;
      int i0 = 0;
      if (ca1[0] != '0')
        i0 = 0;
      else
        for (i0 = 0; i0 < ca1.length; i0++)
          if (ca1[i0] != '0')
            break;
      if (i0 + p < ca1.length - 1)
      {
        carry = checkForCarry(ca1, i0 + p + 1);
        if (carry)
          carry = startSymbolicCarry(ca1, i0 + p, i0);
        if (carry)
        {
          ca2 = new char[i0 + p + 1];
          ca2[i0] = '1';
          for (j = 0; j < i0; j++)
            ca2[j] = '0';
          for (i = i0, j = i0 + 1; j < p + 1; i++, j++)
            ca2[j] = ca1[i];
          expon++;
          ca1 = ca2;
        }
      }
      if (Math.abs(expon) < 100 && !this.optionalL)
        eSize = 4;
      else
        eSize = 5;
      if (this.alternateForm || !this.precisionSet || this.precision != 0)
        ca2 = new char[2 + p + eSize];
      else
        ca2 = new char[1 + eSize];
      if (ca1[0] != '0')
      {
        ca2[0] = ca1[0];
        j = 1;
      } else
      {
        for (j = 1; j < (ePos == -1 ? ca1.length : ePos); j++)
          if (ca1[j] != '0')
            break;
        if ((ePos != -1 && j < ePos) || (ePos == -1 && j < ca1.length))
        {
          ca2[0] = ca1[j];
          expon -= j;
          j++;
        } else
        {
          ca2[0] = '0';
          j = 2;
        }
      }
      if (this.alternateForm || !this.precisionSet || this.precision != 0)
      {
        ca2[1] = '.';
        i = 2;
      } else
        i = 1;
      for (k = 0; k < p && j < ca1.length; j++, i++, k++)
        ca2[i] = ca1[j];
      for (; i < ca2.length - eSize; i++)
        ca2[i] = '0';
      ca2[i++] = eChar;
      if (expon < 0)
        ca2[i++] = '-';
      else
        ca2[i++] = '+';
      expon = Math.abs(expon);
      if (expon >= 100)
      {
        switch (expon / 100)
        {
          case 1:
            ca2[i] = '1';
            break;
          case 2:
            ca2[i] = '2';
            break;
          case 3:
            ca2[i] = '3';
            break;
          case 4:
            ca2[i] = '4';
            break;
          case 5:
            ca2[i] = '5';
            break;
          case 6:
            ca2[i] = '6';
            break;
          case 7:
            ca2[i] = '7';
            break;
          case 8:
            ca2[i] = '8';
            break;
          case 9:
            ca2[i] = '9';
            break;
        }
        i++;
      }
      switch ((expon % 100) / 10)
      {
        case 0:
          ca2[i] = '0';
          break;
        case 1:
          ca2[i] = '1';
          break;
        case 2:
          ca2[i] = '2';
          break;
        case 3:
          ca2[i] = '3';
          break;
        case 4:
          ca2[i] = '4';
          break;
        case 5:
          ca2[i] = '5';
          break;
        case 6:
          ca2[i] = '6';
          break;
        case 7:
          ca2[i] = '7';
          break;
        case 8:
          ca2[i] = '8';
          break;
        case 9:
          ca2[i] = '9';
          break;
      }
      i++;
      switch (expon % 10)
      {
        case 0:
          ca2[i] = '0';
          break;
        case 1:
          ca2[i] = '1';
          break;
        case 2:
          ca2[i] = '2';
          break;
        case 3:
          ca2[i] = '3';
          break;
        case 4:
          ca2[i] = '4';
          break;
        case 5:
          ca2[i] = '5';
          break;
        case 6:
          ca2[i] = '6';
          break;
        case 7:
          ca2[i] = '7';
          break;
        case 8:
          ca2[i] = '8';
          break;
        case 9:
          ca2[i] = '9';
          break;
      }
      int nZeros = 0;
      if (!this.leftJustify && this.leadingZeros)
      {
        int xThousands = 0;
        if (this.thousands)
        {
          int xlead = 0;
          if (ca2[0] == '+' || ca2[0] == '-' || ca2[0] == ' ')
            xlead = 1;
          int xdp = xlead;
          for (; xdp < ca2.length; xdp++)
            if (ca2[xdp] == '.')
              break;
          xThousands = (xdp - xlead) / 3;
        }
        if (this.fieldWidthSet)
          nZeros = this.fieldWidth - ca2.length;
        if ((!minusSign && (this.leadingSign || this.leadingSpace)) || minusSign)
          nZeros--;
        nZeros -= xThousands;
        if (nZeros < 0)
          nZeros = 0;
      }
      j = 0;
      if ((!minusSign && (this.leadingSign || this.leadingSpace)) || minusSign)
      {
        ca3 = new char[ca2.length + nZeros + 1];
        j++;
      } else
        ca3 = new char[ca2.length + nZeros];
      if (!minusSign)
      {
        if (this.leadingSign)
          ca3[0] = '+';
        if (this.leadingSpace)
          ca3[0] = ' ';
      } else
        ca3[0] = '-';
      for (k = 0; k < nZeros; j++, k++)
        ca3[j] = '0';
      for (i = 0; i < ca2.length && j < ca3.length; i++, j++)
        ca3[j] = ca2[i];

      int lead = 0;
      if (ca3[0] == '+' || ca3[0] == '-' || ca3[0] == ' ')
        lead = 1;
      int dp = lead;
      for (; dp < ca3.length; dp++)
        if (ca3[dp] == '.')
          break;
      int nThousands = dp / 3;
      // Localize the decimal point.
      if (dp < ca3.length)
        ca3[dp] = Printf.this.dfs.getDecimalSeparator();
      char[] ca4 = ca3;
      if (this.thousands && nThousands > 0)
      {
        ca4 = new char[ca3.length + nThousands + lead];
        ca4[0] = ca3[0];
        for (i = lead, k = lead; i < dp; i++)
        {
          if (i > 0 && (dp - i) % 3 == 0)
          {
            // ca4[k]=',';
            ca4[k] = Printf.this.dfs.getGroupingSeparator();
            ca4[k + 1] = ca3[i];
            k += 2;
          } else
          {
            ca4[k] = ca3[i];
            k++;
          }
        }
        for (; i < ca3.length; i++, k++)
          ca4[k] = ca3[i];
      }
      return ca4;
    }

    /**
     * Check to see if the digits that are going to be truncated because of the
     * precision should force a round in the preceding digits.
     * 
     * @param ca1
     *          the array of digits
     * @param icarry
     *          the index of the first digit that is to be truncated from the
     *          print
     * @return <code>true</code> if the truncation forces a round that will
     *         change the print
     */
    private boolean checkForCarry(char[] ca1, int icarry)
    {
      boolean carry = false;
      if (icarry < ca1.length)
      {
        if (ca1[icarry] == '6' || ca1[icarry] == '7' || ca1[icarry] == '8' || ca1[icarry] == '9')
          carry = true;
        else if (ca1[icarry] == '5')
        {
          int ii = icarry + 1;
          for (; ii < ca1.length; ii++)
            if (ca1[ii] != '0')
              break;
          carry = ii < ca1.length;
          if (!carry && icarry > 0)
          {
            carry = (ca1[icarry - 1] == '1' || ca1[icarry - 1] == '3' || ca1[icarry - 1] == '5'
                || ca1[icarry - 1] == '7' || ca1[icarry - 1] == '9');
          }
        }
      }
      return carry;
    }

    /**
     * Start the symbolic carry process. The process is not quite finished
     * because the symbolic carry may change the length of the string and change
     * the exponent (in e format).
     * 
     * @param cLast
     *          index of the last digit changed by the round
     * @param cFirst
     *          index of the first digit allowed to be changed by this phase of
     *          the round
     * @return <code>true</code> if the carry forces a round that will change
     *         the print still more
     */
    private boolean startSymbolicCarry(char[] ca, int cLast, int cFirst)
    {
      boolean carry = true;
      for (int i = cLast; carry && i >= cFirst; i--)
      {
        carry = false;
        switch (ca[i])
        {
          case '0':
            ca[i] = '1';
            break;
          case '1':
            ca[i] = '2';
            break;
          case '2':
            ca[i] = '3';
            break;
          case '3':
            ca[i] = '4';
            break;
          case '4':
            ca[i] = '5';
            break;
          case '5':
            ca[i] = '6';
            break;
          case '6':
            ca[i] = '7';
            break;
          case '7':
            ca[i] = '8';
            break;
          case '8':
            ca[i] = '9';
            break;
          case '9':
            ca[i] = '0';
            carry = true;
            break;
        }
      }
      return carry;
    }

    /**
     * An intermediate routine on the way to creating an e format String. The
     * method decides whether the input double value is an infinity,
     * not-a-number, or a finite double and formats each type of input
     * appropriately.
     * 
     * @param x
     *          the double value to be formatted.
     * @param eChar
     *          an 'e' or 'E' to use in the converted double value.
     * @return the converted double value.
     */
    private String eFormatString(double x, char eChar)
    {
      char[] ca4, ca5;
      if (Double.isInfinite(x))
      {
        if (x == Double.POSITIVE_INFINITY)
        {
          if (this.leadingSign)
            ca4 = "+Inf".toCharArray();
          else if (this.leadingSpace)
            ca4 = " Inf".toCharArray();
          else
            ca4 = "Inf".toCharArray();
        } else
          ca4 = "-Inf".toCharArray();
      } else if (Double.isNaN(x))
      {
        if (this.leadingSign)
          ca4 = "+NaN".toCharArray();
        else if (this.leadingSpace)
          ca4 = " NaN".toCharArray();
        else
          ca4 = "NaN".toCharArray();
      } else
        ca4 = eFormatDigits(x, eChar);
      ca5 = applyFloatPadding(ca4, false);
      return new String(ca5);
    }

    /**
     * Apply zero or blank, left or right padding.
     * 
     * @param ca4
     *          array of characters before padding is finished
     * @param noDigits
     *          NaN or signed Inf
     * @return a padded array of characters
     */
    private char[] applyFloatPadding(char[] ca4, boolean noDigits)
    {
      char[] ca5 = ca4;
      if (this.fieldWidthSet)
      {
        int i, j, nBlanks;
        if (this.leftJustify)
        {
          nBlanks = this.fieldWidth - ca4.length;
          if (nBlanks > 0)
          {
            ca5 = new char[ca4.length + nBlanks];
            for (i = 0; i < ca4.length; i++)
              ca5[i] = ca4[i];
            for (j = 0; j < nBlanks; j++, i++)
              ca5[i] = ' ';
          }
        } else if (!this.leadingZeros || noDigits)
        {
          nBlanks = this.fieldWidth - ca4.length;
          if (nBlanks > 0)
          {
            ca5 = new char[ca4.length + nBlanks];
            for (i = 0; i < nBlanks; i++)
              ca5[i] = ' ';
            for (j = 0; j < ca4.length; i++, j++)
              ca5[i] = ca4[j];
          }
        } else if (this.leadingZeros)
        {
          nBlanks = this.fieldWidth - ca4.length;
          if (nBlanks > 0)
          {
            ca5 = new char[ca4.length + nBlanks];
            i = 0;
            j = 0;
            if (ca4[0] == '-')
            {
              ca5[0] = '-';
              i++;
              j++;
            }
            for (int k = 0; k < nBlanks; i++, k++)
              ca5[i] = '0';
            for (; j < ca4.length; i++, j++)
              ca5[i] = ca4[j];
          }
        }
      }
      return ca5;
    }

    /**
     * Format method for the f conversion character.
     * 
     * @param x
     *          the double to format.
     * @return the formatted String.
     */
    private String printFFormat(double x)
    {
      return fFormatString(x);
    }

    /**
     * Format method for the e or E conversion character.
     * 
     * @param x
     *          the double to format.
     * @return the formatted String.
     */
    private String printEFormat(double x)
    {
      if (this.conversionCharacter == 'e')
        return eFormatString(x, 'e');
      return eFormatString(x, 'E');
    }

    /**
     * Format method for the g conversion character.
     * 
     * For g format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. '+' character means that the conversion will always begin with a
     * sign (+ or -). The blank flag character means that a non-negative input
     * will be preceded with a blank. If both a '+' and a ' ' are specified, the
     * blank flag is ignored. The '0' flag character implies that padding to the
     * field width will be done with zeros instead of blanks.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the minimum number of digits to appear after
     * the radix character. Padding is with trailing 0s.
     * 
     * @param x
     *          the double to format.
     * @return the formatted String.
     */
    private String printGFormat(double x)
    {
      String sx, sy, sz, ret;
      int savePrecision = this.precision;
      int i;
      char[] ca4, ca5;
      if (Double.isInfinite(x))
      {
        if (x == Double.POSITIVE_INFINITY)
        {
          if (this.leadingSign)
            ca4 = "+Inf".toCharArray();
          else if (this.leadingSpace)
            ca4 = " Inf".toCharArray();
          else
            ca4 = "Inf".toCharArray();
        } else
          ca4 = "-Inf".toCharArray();
      } else if (Double.isNaN(x))
      {
        if (this.leadingSign)
          ca4 = "+NaN".toCharArray();
        else if (this.leadingSpace)
          ca4 = " NaN".toCharArray();
        else
          ca4 = "NaN".toCharArray();
      } else
      {
        if (!this.precisionSet)
          this.precision = defaultDigits;
        if (this.precision == 0)
          this.precision = 1;
        int ePos = -1;
        if (this.conversionCharacter == 'g')
        {
          sx = eFormatString(x, 'e').trim();
          ePos = sx.indexOf('e');
        } else
        {
          sx = eFormatString(x, 'E').trim();
          ePos = sx.indexOf('E');
        }
        i = ePos + 1;
        int expon = 0;
        if (sx.charAt(i) == '-')
        {
          for (++i; i < sx.length(); i++)
            if (sx.charAt(i) != '0')
              break;
          if (i < sx.length())
            expon = -Integer.parseInt(sx.substring(i));
        } else
        {
          if (sx.charAt(i) == '+')
            ++i;
          for (; i < sx.length(); i++)
            if (sx.charAt(i) != '0')
              break;
          if (i < sx.length())
            expon = Integer.parseInt(sx.substring(i));
        }
        // Trim trailing zeros.
        // If the radix character is not followed by
        // a digit, trim it, too.
        if (!this.alternateForm)
        {
          if (expon >= -4 && expon < this.precision)
            sy = fFormatString(x).trim();
          else
            sy = sx.substring(0, ePos);
          i = sy.length() - 1;
          for (; i >= 0; i--)
            if (sy.charAt(i) != '0')
              break;
          if (i >= 0 && sy.charAt(i) == '.')
            i--;
          if (i == -1)
            sz = "0";
          else if (!Character.isDigit(sy.charAt(i)))
            sz = sy.substring(0, i + 1) + "0";
          else
            sz = sy.substring(0, i + 1);
          if (expon >= -4 && expon < this.precision)
            ret = sz;
          else
            ret = sz + sx.substring(ePos);
        } else
        {
          if (expon >= -4 && expon < this.precision)
            ret = fFormatString(x).trim();
          else
            ret = sx;
        }
        // leading space was trimmed off during
        // construction
        if (this.leadingSpace)
          if (x >= 0)
            ret = " " + ret;
        ca4 = ret.toCharArray();
      }
      // Pad with blanks or zeros.
      ca5 = applyFloatPadding(ca4, false);
      this.precision = savePrecision;
      return new String(ca5);
    }

    /**
     * Format method for the d conversion specifer and short argument.
     * 
     * For d format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. A '+' character means that the conversion will always begin with a
     * sign (+ or -). The blank flag character means that a non-negative input
     * will be preceded with a blank. If both a '+' and a ' ' are specified, the
     * blank flag is ignored. The '0' flag character implies that padding to the
     * field width will be done with zeros instead of blanks.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the minimum number of digits to appear. Padding
     * is with leading 0s.
     * 
     * @param x
     *          the short to format.
     * @return the formatted String.
     */
    private String printDFormat(short x)
    {
      return printDFormat(Short.toString(x));
    }

    /**
     * Format method for the d conversion character and long argument.
     * 
     * For d format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. A '+' character means that the conversion will always begin with a
     * sign (+ or -). The blank flag character means that a non-negative input
     * will be preceded with a blank. If both a '+' and a ' ' are specified, the
     * blank flag is ignored. The '0' flag character implies that padding to the
     * field width will be done with zeros instead of blanks.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the minimum number of digits to appear. Padding
     * is with leading 0s.
     * 
     * @param x
     *          the long to format.
     * @return the formatted String.
     */
    private String printDFormat(long x)
    {
      return printDFormat(Long.toString(x));
    }

    /**
     * Format method for the d conversion character and int argument.
     * 
     * For d format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. A '+' character means that the conversion will always begin with a
     * sign (+ or -). The blank flag character means that a non-negative input
     * will be preceded with a blank. If both a '+' and a ' ' are specified, the
     * blank flag is ignored. The '0' flag character implies that padding to the
     * field width will be done with zeros instead of blanks.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the minimum number of digits to appear. Padding
     * is with leading 0s.
     * 
     * @param x
     *          the int to format.
     * @return the formatted String.
     */
    private String printDFormat(int x)
    {
      return printDFormat(Integer.toString(x));
    }

    /**
     * Utility method for formatting using the d conversion character.
     * 
     * @param sx
     *          the String to format, the result of converting a short, int, or
     *          long to a String.
     * @return the formatted String.
     */
    private String printDFormat(String Sx)
    {
      int nLeadingZeros = 0;
      int nBlanks = 0, n = 0;
      int i = 0, jFirst = 0;
      String sx = Sx;
      boolean neg = sx.charAt(0) == '-';

      if (sx.equals("0") && this.precisionSet && this.precision == 0)
        sx = "";
      if (!neg)
      {
        if (this.precisionSet && sx.length() < this.precision)
          nLeadingZeros = this.precision - sx.length();
      } else
      {
        if (this.precisionSet && (sx.length() - 1) < this.precision)
          nLeadingZeros = this.precision - sx.length() + 1;
      }
      if (nLeadingZeros < 0)
        nLeadingZeros = 0;
      if (this.fieldWidthSet)
      {
        nBlanks = this.fieldWidth - nLeadingZeros - sx.length();
        if (!neg && (this.leadingSign || this.leadingSpace))
          nBlanks--;
      }
      if (nBlanks < 0)
        nBlanks = 0;
      if (this.leadingSign)
        n++;
      else if (this.leadingSpace)
        n++;
      n += nBlanks;
      n += nLeadingZeros;
      n += sx.length();
      char[] ca = new char[n];
      if (this.leftJustify)
      {
        if (neg)
          ca[i++] = '-';
        else if (this.leadingSign)
          ca[i++] = '+';
        else if (this.leadingSpace)
          ca[i++] = ' ';
        char[] csx = sx.toCharArray();
        jFirst = neg ? 1 : 0;
        for (int j = 0; j < nLeadingZeros; i++, j++)
          ca[i] = '0';
        for (int j = jFirst; j < csx.length; j++, i++)
          ca[i] = csx[j];
        for (int j = 0; j < nBlanks; i++, j++)
          ca[i] = ' ';
      } else
      {
        if (!this.leadingZeros)
        {
          for (i = 0; i < nBlanks; i++)
            ca[i] = ' ';
          if (neg)
            ca[i++] = '-';
          else if (this.leadingSign)
            ca[i++] = '+';
          else if (this.leadingSpace)
            ca[i++] = ' ';
        } else
        {
          if (neg)
            ca[i++] = '-';
          else if (this.leadingSign)
            ca[i++] = '+';
          else if (this.leadingSpace)
            ca[i++] = ' ';
          for (int j = 0; j < nBlanks; j++, i++)
            ca[i] = '0';
        }
        for (int j = 0; j < nLeadingZeros; j++, i++)
          ca[i] = '0';
        char[] csx = sx.toCharArray();
        jFirst = neg ? 1 : 0;
        for (int j = jFirst; j < csx.length; j++, i++)
          ca[i] = csx[j];
      }
      return new String(ca);
    }

    /**
     * Format method for the x conversion character and short argument.
     * 
     * For x format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. The '#' flag character means to lead with '0x'.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the minimum number of digits to appear. Padding
     * is with leading 0s.
     * 
     * @param x
     *          the short to format.
     * @return the formatted String.
     */
    private String printXFormat(short x)
    {
      String sx = null;
      if (x == Short.MIN_VALUE)
        sx = "8000";
      else if (x < 0)
      {
        String t;
        if (x == Short.MIN_VALUE)
          t = "0";
        else
        {
          t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 16);
          if (t.charAt(0) == 'F' || t.charAt(0) == 'f')
            t = t.substring(16, 32);
        }
        switch (t.length())
        {
          case 1:
            sx = "800" + t;
            break;
          case 2:
            sx = "80" + t;
            break;
          case 3:
            sx = "8" + t;
            break;
          case 4:
            switch (t.charAt(0))
            {
              case '1':
                sx = "9" + t.substring(1, 4);
                break;
              case '2':
                sx = "a" + t.substring(1, 4);
                break;
              case '3':
                sx = "b" + t.substring(1, 4);
                break;
              case '4':
                sx = "c" + t.substring(1, 4);
                break;
              case '5':
                sx = "d" + t.substring(1, 4);
                break;
              case '6':
                sx = "e" + t.substring(1, 4);
                break;
              case '7':
                sx = "f" + t.substring(1, 4);
                break;
            }
            break;
        }
      } else
        sx = Integer.toString(x, 16);
      return printXFormat(sx);
    }

    /**
     * Format method for the x conversion character and long argument.
     * 
     * For x format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. The '#' flag character means to lead with '0x'.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the minimum number of digits to appear. Padding
     * is with leading 0s.
     * 
     * @param x
     *          the long to format.
     * @return the formatted String.
     */
    private String printXFormat(long x)
    {
      String sx = null;
      if (x == Long.MIN_VALUE)
        sx = "8000000000000000";
      else if (x < 0)
      {
        String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 16);
        switch (t.length())
        {
          case 1:
            sx = "800000000000000" + t;
            break;
          case 2:
            sx = "80000000000000" + t;
            break;
          case 3:
            sx = "8000000000000" + t;
            break;
          case 4:
            sx = "800000000000" + t;
            break;
          case 5:
            sx = "80000000000" + t;
            break;
          case 6:
            sx = "8000000000" + t;
            break;
          case 7:
            sx = "800000000" + t;
            break;
          case 8:
            sx = "80000000" + t;
            break;
          case 9:
            sx = "8000000" + t;
            break;
          case 10:
            sx = "800000" + t;
            break;
          case 11:
            sx = "80000" + t;
            break;
          case 12:
            sx = "8000" + t;
            break;
          case 13:
            sx = "800" + t;
            break;
          case 14:
            sx = "80" + t;
            break;
          case 15:
            sx = "8" + t;
            break;
          case 16:
            switch (t.charAt(0))
            {
              case '1':
                sx = "9" + t.substring(1, 16);
                break;
              case '2':
                sx = "a" + t.substring(1, 16);
                break;
              case '3':
                sx = "b" + t.substring(1, 16);
                break;
              case '4':
                sx = "c" + t.substring(1, 16);
                break;
              case '5':
                sx = "d" + t.substring(1, 16);
                break;
              case '6':
                sx = "e" + t.substring(1, 16);
                break;
              case '7':
                sx = "f" + t.substring(1, 16);
                break;
            }
            break;
        }
      } else
        sx = Long.toString(x, 16);
      return printXFormat(sx);
    }

    /**
     * Format method for the x conversion character and int argument.
     * 
     * For x format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. The '#' flag character means to lead with '0x'.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the minimum number of digits to appear. Padding
     * is with leading 0s.
     * 
     * @param x
     *          the int to format.
     * @return the formatted String.
     */
    private String printXFormat(int x)
    {
      String sx = null;
      if (x == Integer.MIN_VALUE)
        sx = "80000000";
      else if (x < 0)
      {
        String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 16);
        switch (t.length())
        {
          case 1:
            sx = "8000000" + t;
            break;
          case 2:
            sx = "800000" + t;
            break;
          case 3:
            sx = "80000" + t;
            break;
          case 4:
            sx = "8000" + t;
            break;
          case 5:
            sx = "800" + t;
            break;
          case 6:
            sx = "80" + t;
            break;
          case 7:
            sx = "8" + t;
            break;
          case 8:
            switch (t.charAt(0))
            {
              case '1':
                sx = "9" + t.substring(1, 8);
                break;
              case '2':
                sx = "a" + t.substring(1, 8);
                break;
              case '3':
                sx = "b" + t.substring(1, 8);
                break;
              case '4':
                sx = "c" + t.substring(1, 8);
                break;
              case '5':
                sx = "d" + t.substring(1, 8);
                break;
              case '6':
                sx = "e" + t.substring(1, 8);
                break;
              case '7':
                sx = "f" + t.substring(1, 8);
                break;
            }
            break;
        }
      } else
        sx = Integer.toString(x, 16);
      return printXFormat(sx);
    }

    /**
     * Utility method for formatting using the x conversion character.
     * 
     * @param sx
     *          the String to format, the result of converting a short, int, or
     *          long to a String.
     * @return the formatted String.
     */
    private String printXFormat(String Sx)
    {
      int nLeadingZeros = 0;
      int nBlanks = 0;
      String sx = Sx;
      if (sx.equals("0") && this.precisionSet && this.precision == 0)
        sx = "";
      if (this.precisionSet)
        nLeadingZeros = this.precision - sx.length();
      if (nLeadingZeros < 0)
        nLeadingZeros = 0;
      if (this.fieldWidthSet)
      {
        nBlanks = this.fieldWidth - nLeadingZeros - sx.length();
        if (this.alternateForm)
          nBlanks = nBlanks - 2;
      }
      if (nBlanks < 0)
        nBlanks = 0;
      int n = 0;
      if (this.alternateForm)
        n += 2;
      n += nLeadingZeros;
      n += sx.length();
      n += nBlanks;
      char[] ca = new char[n];
      int i = 0;
      if (this.leftJustify)
      {
        if (this.alternateForm)
        {
          ca[i++] = '0';
          ca[i++] = 'x';
        }
        for (int j = 0; j < nLeadingZeros; j++, i++)
          ca[i] = '0';
        char[] csx = sx.toCharArray();
        for (int j = 0; j < csx.length; j++, i++)
          ca[i] = csx[j];
        for (int j = 0; j < nBlanks; j++, i++)
          ca[i] = ' ';
      } else
      {
        if (!this.leadingZeros)
          for (int j = 0; j < nBlanks; j++, i++)
            ca[i] = ' ';
        if (this.alternateForm)
        {
          ca[i++] = '0';
          ca[i++] = 'x';
        }
        if (this.leadingZeros)
          for (int j = 0; j < nBlanks; j++, i++)
            ca[i] = '0';
        for (int j = 0; j < nLeadingZeros; j++, i++)
          ca[i] = '0';
        char[] csx = sx.toCharArray();
        for (int j = 0; j < csx.length; j++, i++)
          ca[i] = csx[j];
      }
      String caReturn = new String(ca);
      if (this.conversionCharacter == 'X')
        caReturn = caReturn.toUpperCase();
      return caReturn;
    }

    /**
     * Format method for the o conversion character and short argument.
     * 
     * For o format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. The '#' flag character means that the output begins with a leading
     * 0 and the precision is increased by 1.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the minimum number of digits to appear. Padding
     * is with leading 0s.
     * 
     * @param x
     *          the short to format.
     * @return the formatted String.
     */
    private String printOFormat(short x)
    {
      String sx = null;
      if (x == Short.MIN_VALUE)
        sx = "100000";
      else if (x < 0)
      {
        String t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 8);
        switch (t.length())
        {
          case 1:
            sx = "10000" + t;
            break;
          case 2:
            sx = "1000" + t;
            break;
          case 3:
            sx = "100" + t;
            break;
          case 4:
            sx = "10" + t;
            break;
          case 5:
            sx = "1" + t;
            break;
        }
      } else
        sx = Integer.toString(x, 8);
      return printOFormat(sx);
    }

    /**
     * Format method for the o conversion character and long argument.
     * 
     * For o format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. The '#' flag character means that the output begins with a leading
     * 0 and the precision is increased by 1.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the minimum number of digits to appear. Padding
     * is with leading 0s.
     * 
     * @param x
     *          the long to format.
     * @return the formatted String.
     */
    private String printOFormat(long x)
    {
      String sx = null;
      if (x == Long.MIN_VALUE)
        sx = "1000000000000000000000";
      else if (x < 0)
      {
        String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 8);
        switch (t.length())
        {
          case 1:
            sx = "100000000000000000000" + t;
            break;
          case 2:
            sx = "10000000000000000000" + t;
            break;
          case 3:
            sx = "1000000000000000000" + t;
            break;
          case 4:
            sx = "100000000000000000" + t;
            break;
          case 5:
            sx = "10000000000000000" + t;
            break;
          case 6:
            sx = "1000000000000000" + t;
            break;
          case 7:
            sx = "100000000000000" + t;
            break;
          case 8:
            sx = "10000000000000" + t;
            break;
          case 9:
            sx = "1000000000000" + t;
            break;
          case 10:
            sx = "100000000000" + t;
            break;
          case 11:
            sx = "10000000000" + t;
            break;
          case 12:
            sx = "1000000000" + t;
            break;
          case 13:
            sx = "100000000" + t;
            break;
          case 14:
            sx = "10000000" + t;
            break;
          case 15:
            sx = "1000000" + t;
            break;
          case 16:
            sx = "100000" + t;
            break;
          case 17:
            sx = "10000" + t;
            break;
          case 18:
            sx = "1000" + t;
            break;
          case 19:
            sx = "100" + t;
            break;
          case 20:
            sx = "10" + t;
            break;
          case 21:
            sx = "1" + t;
            break;
        }
      } else
        sx = Long.toString(x, 8);
      return printOFormat(sx);
    }

    /**
     * Format method for the o conversion character and int argument.
     * 
     * For o format, the flag character '-', means that the output should be
     * left justified within the field. The default is to pad with blanks on the
     * left. The '#' flag character means that the output begins with a leading
     * 0 and the precision is increased by 1.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is to add no padding. Padding is with blanks by
     * default.
     * 
     * The precision, if set, is the minimum number of digits to appear. Padding
     * is with leading 0s.
     * 
     * @param x
     *          the int to format.
     * @return the formatted String.
     */
    private String printOFormat(int x)
    {
      String sx = null;
      if (x == Integer.MIN_VALUE)
        sx = "20000000000";
      else if (x < 0)
      {
        String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 8);
        switch (t.length())
        {
          case 1:
            sx = "2000000000" + t;
            break;
          case 2:
            sx = "200000000" + t;
            break;
          case 3:
            sx = "20000000" + t;
            break;
          case 4:
            sx = "2000000" + t;
            break;
          case 5:
            sx = "200000" + t;
            break;
          case 6:
            sx = "20000" + t;
            break;
          case 7:
            sx = "2000" + t;
            break;
          case 8:
            sx = "200" + t;
            break;
          case 9:
            sx = "20" + t;
            break;
          case 10:
            sx = "2" + t;
            break;
          case 11:
            sx = "3" + t.substring(1);
            break;
        }
      } else
        sx = Integer.toString(x, 8);
      return printOFormat(sx);
    }

    /**
     * Utility method for formatting using the o conversion character.
     * 
     * @param sx
     *          the String to format, the result of converting a short, int, or
     *          long to a String.
     * @return the formatted String.
     */
    private String printOFormat(String Sx)
    {
      int nLeadingZeros = 0;
      int nBlanks = 0;
      String sx = Sx;
      if (sx.equals("0") && this.precisionSet && this.precision == 0)
        sx = "";
      if (this.precisionSet)
        nLeadingZeros = this.precision - sx.length();
      if (this.alternateForm)
        nLeadingZeros++;
      if (nLeadingZeros < 0)
        nLeadingZeros = 0;
      if (this.fieldWidthSet)
        nBlanks = this.fieldWidth - nLeadingZeros - sx.length();
      if (nBlanks < 0)
        nBlanks = 0;
      int n = nLeadingZeros + sx.length() + nBlanks;
      char[] ca = new char[n];
      int i;
      if (this.leftJustify)
      {
        for (i = 0; i < nLeadingZeros; i++)
          ca[i] = '0';
        char[] csx = sx.toCharArray();
        for (int j = 0; j < csx.length; j++, i++)
          ca[i] = csx[j];
        for (int j = 0; j < nBlanks; j++, i++)
          ca[i] = ' ';
      } else
      {
        if (this.leadingZeros)
          for (i = 0; i < nBlanks; i++)
            ca[i] = '0';
        else
          for (i = 0; i < nBlanks; i++)
            ca[i] = ' ';
        for (int j = 0; j < nLeadingZeros; j++, i++)
          ca[i] = '0';
        char[] csx = sx.toCharArray();
        for (int j = 0; j < csx.length; j++, i++)
          ca[i] = csx[j];
      }
      return new String(ca);
    }

    /**
     * Format method for the c conversion character and char argument.
     * 
     * The only flag character that affects c format is the '-', meaning that
     * the output should be left justified within the field. The default is to
     * pad with blanks on the left.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. Padding is with blanks by default. The default width is 1.
     * 
     * The precision, if set, is ignored.
     * 
     * @param x
     *          the char to format.
     * @return the formatted String.
     */
    private String printCFormat(char x)
    {
      int nPrint = 1;
      int width = this.fieldWidth;
      if (!this.fieldWidthSet)
        width = nPrint;
      char[] ca = new char[width];
      int i = 0;
      if (this.leftJustify)
      {
        ca[0] = x;
        for (i = 1; i <= width - nPrint; i++)
          ca[i] = ' ';
      } else
      {
        for (i = 0; i < width - nPrint; i++)
          ca[i] = ' ';
        ca[i] = x;
      }
      return new String(ca);
    }

    /**
     * Format method for the s conversion character and String argument.
     * 
     * The only flag character that affects s format is the '-', meaning that
     * the output should be left justified within the field. The default is to
     * pad with blanks on the left.
     * 
     * The field width is treated as the minimum number of characters to be
     * printed. The default is the smaller of the number of characters in the
     * the input and the precision. Padding is with blanks by default.
     * 
     * The precision, if set, specifies the maximum number of characters to be
     * printed from the string. A null digit string is treated as a 0. The
     * default is not to set a maximum number of characters to be printed.
     * 
     * @param x
     *          the String to format.
     * @return the formatted String.
     */
    private String printSFormat(String x)
    {
      int nPrint = x.length();
      int width = this.fieldWidth;
      if (this.precisionSet && nPrint > this.precision)
        nPrint = this.precision;
      if (!this.fieldWidthSet)
        width = nPrint;
      int n = 0;
      if (width > nPrint)
        n += width - nPrint;
      if (nPrint >= x.length())
        n += x.length();
      else
        n += nPrint;
      char[] ca = new char[n];
      int i = 0;
      if (this.leftJustify)
      {
        if (nPrint >= x.length())
        {
          char[] csx = x.toCharArray();
          for (i = 0; i < x.length(); i++)
            ca[i] = csx[i];
        } else
        {
          char[] csx = x.substring(0, nPrint).toCharArray();
          for (i = 0; i < nPrint; i++)
            ca[i] = csx[i];
        }
        for (int j = 0; j < width - nPrint; j++, i++)
          ca[i] = ' ';
      } else
      {
        for (i = 0; i < width - nPrint; i++)
          ca[i] = ' ';
        if (nPrint >= x.length())
        {
          char[] csx = x.toCharArray();
          for (int j = 0; j < x.length(); i++, j++)
            ca[i] = csx[j];
        } else
        {
          char[] csx = x.substring(0, nPrint).toCharArray();
          for (int j = 0; j < nPrint; i++, j++)
            ca[i] = csx[j];
        }
      }
      return new String(ca);
    }

    /**
     * Check for a conversion character. If it is there, store it.
     * 
     * @param x
     *          the String to format.
     * @return <code>true</code> if the conversion character is there, and
     *         <code>false</code> otherwise.
     */
    private boolean setConversionCharacter()
    {
      /* idfgGoxXeEcs */
      boolean ret = false;
      this.conversionCharacter = '\0';
      if (this.pos < this.fmt.length())
      {
        char c = this.fmt.charAt(this.pos);
        if (c == 'i' || c == 'd' || c == 'f' || c == 'g' || c == 'G' || c == 'o' || c == 'x'
            || c == 'X' || c == 'e' || c == 'E' || c == 'c' || c == 's' || c == '%')
        {
          this.conversionCharacter = c;
          this.pos++;
          ret = true;
        }
      }
      return ret;
    }

    /**
     * Check for an h, l, or L in a format. An L is used to control the minimum
     * number of digits in an exponent when using floating point formats. An l
     * or h is used to control conversion of the input to a long or short,
     * respectively, before formatting. If any of these is present, store them.
     */
    private void setOptionalHL()
    {
      this.optionalh = false;
      this.optionall = false;
      this.optionalL = false;
      if (this.pos < this.fmt.length())
      {
        char c = this.fmt.charAt(this.pos);
        if (c == 'h')
        {
          this.optionalh = true;
          this.pos++;
        } else if (c == 'l')
        {
          this.optionall = true;
          this.pos++;
        } else if (c == 'L')
        {
          this.optionalL = true;
          this.pos++;
        }
      }
    }

    /**
     * Set the precision.
     */
    private void setPrecision()
    {
      int firstPos = this.pos;
      this.precisionSet = false;
      if (this.pos < this.fmt.length() && this.fmt.charAt(this.pos) == '.')
      {
        this.pos++;
        if ((this.pos < this.fmt.length()) && (this.fmt.charAt(this.pos) == '*'))
        {
          this.pos++;
          if (!setPrecisionArgPosition())
          {
            this.variablePrecision = true;
            this.precisionSet = true;
          }
          return;
        }
        while (this.pos < this.fmt.length())
        {
          char c = this.fmt.charAt(this.pos);
          if (Character.isDigit(c))
            this.pos++;
          else
            break;
        }
        if (this.pos > firstPos + 1)
        {
          String sz = this.fmt.substring(firstPos + 1, this.pos);
          this.precision = Integer.parseInt(sz);
          this.precisionSet = true;
        }
      }
    }

    /**
     * Set the field width.
     */
    private void setFieldWidth()
    {
      int firstPos = this.pos;
      this.fieldWidth = 0;
      this.fieldWidthSet = false;
      if ((this.pos < this.fmt.length()) && (this.fmt.charAt(this.pos) == '*'))
      {
        this.pos++;
        if (!setFieldWidthArgPosition())
        {
          this.variableFieldWidth = true;
          this.fieldWidthSet = true;
        }
      } else
      {
        while (this.pos < this.fmt.length())
        {
          char c = this.fmt.charAt(this.pos);
          if (Character.isDigit(c))
            this.pos++;
          else
            break;
        }
        if (firstPos < this.pos && firstPos < this.fmt.length())
        {
          String sz = this.fmt.substring(firstPos, this.pos);
          this.fieldWidth = Integer.parseInt(sz);
          this.fieldWidthSet = true;
        }
      }
    }

    /**
     * Store the digits <code>n</code> in %n$ forms.
     */
    private void setArgPosition()
    {
      int xPos;
      for (xPos = this.pos; xPos < this.fmt.length(); xPos++)
      {
        if (!Character.isDigit(this.fmt.charAt(xPos)))
          break;
      }
      if (xPos > this.pos && xPos < this.fmt.length())
      {
        if (this.fmt.charAt(xPos) == '$')
        {
          this.positionalSpecification = true;
          this.argumentPosition = Integer.parseInt(this.fmt.substring(this.pos, xPos));
          this.pos = xPos + 1;
        }
      }
    }

    /**
     * Store the digits <code>n</code> in *n$ forms.
     */
    private boolean setFieldWidthArgPosition()
    {
      boolean ret = false;
      int xPos;
      for (xPos = this.pos; xPos < this.fmt.length(); xPos++)
      {
        if (!Character.isDigit(this.fmt.charAt(xPos)))
          break;
      }
      if (xPos > this.pos && xPos < this.fmt.length())
      {
        if (this.fmt.charAt(xPos) == '$')
        {
          this.positionalFieldWidth = true;
          this.argumentPositionForFieldWidth = Integer.parseInt(this.fmt.substring(this.pos, xPos));
          this.pos = xPos + 1;
          ret = true;
        }
      }
      return ret;
    }

    /**
     * Store the digits <code>n</code> in *n$ forms.
     */
    private boolean setPrecisionArgPosition()
    {
      boolean ret = false;
      int xPos;
      for (xPos = this.pos; xPos < this.fmt.length(); xPos++)
      {
        if (!Character.isDigit(this.fmt.charAt(xPos)))
          break;
      }
      if (xPos > this.pos && xPos < this.fmt.length())
      {
        if (this.fmt.charAt(xPos) == '$')
        {
          this.positionalPrecision = true;
          this.argumentPositionForPrecision = Integer.parseInt(this.fmt.substring(this.pos, xPos));
          this.pos = xPos + 1;
          ret = true;
        }
      }
      return ret;
    }

    boolean isPositionalSpecification()
    {
      return this.positionalSpecification;
    }

    int getArgumentPosition()
    {
      return this.argumentPosition;
    }

    boolean isPositionalFieldWidth()
    {
      return this.positionalFieldWidth;
    }

    int getArgumentPositionForFieldWidth()
    {
      return this.argumentPositionForFieldWidth;
    }

    boolean isPositionalPrecision()
    {
      return this.positionalPrecision;
    }

    int getArgumentPositionForPrecision()
    {
      return this.argumentPositionForPrecision;
    }

    /**
     * Set flag characters, one of '-+#0 or a space.
     */
    private void setFlagCharacters()
    {
      /* '-+ #0 */
      this.thousands = false;
      this.leftJustify = false;
      this.leadingSign = false;
      this.leadingSpace = false;
      this.alternateForm = false;
      this.leadingZeros = false;
      for (; this.pos < this.fmt.length(); this.pos++)
      {
        char c = this.fmt.charAt(this.pos);
        if (c == '\'')
          this.thousands = true;
        else if (c == '-')
        {
          this.leftJustify = true;
          this.leadingZeros = false;
        } else if (c == '+')
        {
          this.leadingSign = true;
          this.leadingSpace = false;
        } else if (c == ' ')
        {
          if (!this.leadingSign)
            this.leadingSpace = true;
        } else if (c == '#')
          this.alternateForm = true;
        else if (c == '0')
        {
          if (!this.leftJustify)
            this.leadingZeros = true;
        } else
          break;
      }
    }

    /**
     * The integer portion of the result of a decimal conversion (i, d, u, f, g,
     * or G) will be formatted with thousands' grouping characters. For other
     * conversions the flag is ignored.
     */
    protected boolean thousands = false;
    /**
     * The result of the conversion will be left-justified within the field.
     */
    protected boolean leftJustify = false;
    /**
     * The result of a signed conversion will always begin with a sign (+ or -).
     */
    protected boolean leadingSign = false;
    /**
     * Flag indicating that left padding with spaces is specified.
     */
    protected boolean leadingSpace = false;
    /**
     * For an o conversion, increase the precision to force the first digit of
     * the result to be a zero. For x (or X) conversions, a non-zero result will
     * have 0x (or 0X) prepended to it. For e, E, f, g, or G conversions, the
     * result will always contain a radix character, even if no digits follow
     * the point. For g and G conversions, trailing zeros will not be removed
     * from the result.
     */
    protected boolean alternateForm = false;
    /**
     * Flag indicating that left padding with zeroes is specified.
     */
    protected boolean leadingZeros = false;
    /**
     * Flag indicating that the field width is *.
     */
    protected boolean variableFieldWidth = false;
    /**
     * If the converted value has fewer bytes than the field width, it will be
     * padded with spaces or zeroes.
     */
    protected int fieldWidth = 0;
    /**
     * Flag indicating whether or not the field width has been set.
     */
    protected boolean fieldWidthSet = false;
    /**
     * The minimum number of digits to appear for the d, i, o, u, x, or X
     * conversions. The number of digits to appear after the radix character for
     * the e, E, and f conversions. The maximum number of significant digits for
     * the g and G conversions. The maximum number of bytes to be printed from a
     * string in s and S conversions.
     */
    protected int precision = 0;
    /** Default precision. */
    protected final static int defaultDigits = 6;
    /**
     * Flag indicating that the precision is *.
     */
    protected boolean variablePrecision = false;
    /**
     * Flag indicating whether or not the precision has been set.
     */
    private boolean precisionSet = false;
    /*
     */
    private boolean positionalSpecification = false;
    private int argumentPosition = 0;
    private boolean positionalFieldWidth = false;
    private int argumentPositionForFieldWidth = 0;
    private boolean positionalPrecision = false;
    private int argumentPositionForPrecision = 0;
    /**
     * Flag specifying that a following d, i, o, u, x, or X conversion character
     * applies to a type short int.
     */
    private boolean optionalh = false;
    /**
     * Flag specifying that a following d, i, o, u, x, or X conversion character
     * applies to a type lont int argument.
     */
    private boolean optionall = false;
    /**
     * Flag specifying that a following e, E, f, g, or G conversion character
     * applies to a type double argument. This is a noop in Java.
     */
    private boolean optionalL = false;
    /** Control string type. */
    private char conversionCharacter = '\0';
    /**
     * Position within the control string. Used by the constructor.
     */
    private int pos = 0;
    /** Literal or control format string. */
    private String fmt;
  }

  /** Vector of control strings and format literals. */
  protected Vector vFmt = new Vector();
  /** Character position. Used by the constructor. */
  protected int cPos = 0;
  /** Character position. Used by the constructor. */
  protected DecimalFormatSymbols dfs = null;
}
